了解如何使用 WebXR 命中测试管理器,通过光线投射创建交互式和沉浸式 AR/VR 体验。发现实现技术、最佳实践和优化策略。
WebXR 命中测试管理器:为沉浸式体验实现光线投射系统
增强现实 (AR) 和虚拟现实 (VR) 技术的兴起为创造身临其境的交互式数字体验开辟了令人兴奋的新可能性。 WebXR 是一个用于访问 Web 浏览器中 VR 和 AR 功能的 JavaScript API,它使全球开发人员能够在多种设备上构建这些体验。 创建引人注目的 WebXR 体验的一个关键组成部分是与虚拟环境交互的能力。 这就是 WebXR 命中测试管理器和光线投射发挥作用的地方。
什么是光线投射?为什么它很重要?
在 WebXR 的背景下,光线投射是一种用于确定虚拟光线(一条直线)是否与 AR 系统检测到的真实世界表面或 VR 环境中的虚拟对象相交的技术。 可以将其想象成将激光笔照到您的周围环境中,然后查看它击中的地方。 WebXR 命中测试管理器提供了执行这些光线投射和检索结果的工具。 此信息对于各种交互至关重要,包括:
- 对象放置: 允许用户将虚拟对象放置到真实世界的表面上,例如将虚拟椅子放在他们的客厅里 (AR)。 考虑一下东京的一位用户在承诺购买家具之前,先虚拟地装饰他们的公寓。
- 目标定位和选择: 允许用户使用虚拟指针或手 (AR/VR) 选择虚拟对象或与 UI 元素交互。 想象一下,伦敦的一位外科医生使用 AR 将解剖信息叠加到患者身上,选择特定区域进行检查。
- 导航: 通过指向一个位置并指示用户移动到那里 (VR) 来移动用户在虚拟世界中的化身。 巴黎的一家博物馆可能会使用 VR 让参观者在历史展览中导航。
- 手势识别: 将命中测试与手部跟踪相结合,以解释用户手势,例如捏合缩放或滑动滚动 (AR/VR)。 这可以在悉尼的协作设计会议中使用,参与者可以在其中一起操作虚拟模型。
了解 WebXR 命中测试管理器
WebXR 命中测试管理器是促进光线投射的 WebXR API 的重要组成部分。 它提供了创建和管理命中测试源的方法,这些源定义了光线的原点和方向。 然后,管理器使用这些源来对真实世界(在 AR 中)或虚拟世界(在 VR 中)执行命中测试,并返回有关任何交点的信息。 关键概念包括:
- XRFrame: XRFrame 表示 XR 场景的时间快照,包括查看器的姿势以及任何检测到的平面或特征。 命中测试是针对 XRFrame 执行的。
- XRHitTestSource: 代表要投射的光线的源。 它定义了原点(光线开始的位置)和方向(光线的指向)。 您通常会为每个输入方法(例如,控制器、手)创建一个 XRHitTestSource。
- XRHitTestResult: 包含有关成功命中的信息,包括交点的姿势(位置和方向)以及与光线原点的距离。
- XRHitTestTrackable: 代表真实世界中被跟踪的特征(如平面)。
实现基本的命中测试系统
让我们逐步介绍使用 JavaScript 实现基本 WebXR 命中测试系统的步骤。 此示例侧重于 AR 对象放置,但这些原理可以适用于其他交互场景。
第 1 步:请求 WebXR 会话和命中测试支持
首先,您需要请求一个 WebXR 会话并确保启用“命中测试”功能。 使用命中测试管理器需要此功能。
async function initXR() {
try {
xrSession = await navigator.xr.requestSession('immersive-ar', {
requiredFeatures: ['hit-test'],
});
xrSession.addEventListener('end', () => {
console.log('XR session ended');
});
// Initialize your WebGL renderer and scene here
initRenderer();
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(xrSession, renderer.getContext())
});
xrReferenceSpace = await xrSession.requestReferenceSpace('local');
xrHitTestSource = await xrSession.requestHitTestSource({
space: xrReferenceSpace
});
xrSession.requestAnimationFrame(renderLoop);
} catch (e) {
console.error('WebXR failed to initialize', e);
}
}
说明:
- `navigator.xr.requestSession('immersive-ar', ...)`:请求沉浸式 AR 会话。 第一个参数指定会话类型('immersive-ar' 用于 AR,'immersive-vr' 用于 VR)。
- `requiredFeatures: ['hit-test']`: 重要的是,请求“命中测试”功能,启用命中测试管理器。
- `xrSession.requestHitTestSource(...)`: 创建一个 XRHitTestSource,定义光线的原点和方向。 在这个基本示例中,我们使用“查看器”参考空间,它对应于用户视点。
第 2 步:创建渲染循环
渲染循环是您的 WebXR 应用程序的核心。 它是您更新场景并渲染每一帧的地方。 在渲染循环中,您将执行命中测试并更新虚拟对象的位置。
function renderLoop(time, frame) {
xrSession.requestAnimationFrame(renderLoop);
const xrFrame = frame;
const xrPose = xrFrame.getViewerPose(xrReferenceSpace);
if (xrPose) {
const hitTestResults = xrFrame.getHitTestResults(xrHitTestSource);
if (hitTestResults.length > 0) {
const hit = hitTestResults[0];
const hitPose = hit.getPose(xrReferenceSpace);
// Update the position and orientation of your virtual object
object3D.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z);
object3D.quaternion.set(hitPose.transform.orientation.x, hitPose.transform.orientation.y, hitPose.transform.orientation.z, hitPose.transform.orientation.w);
object3D.visible = true; // Make the object visible when a hit is found
} else {
object3D.visible = false; // Hide the object if no hit is found
}
}
renderer.render(scene, camera);
}
说明:
- `xrFrame.getHitTestResults(xrHitTestSource)`: 使用先前创建的 XRHitTestSource 执行命中测试。 它返回一个 XRHitTestResult 对象数组,表示找到的所有交点。
- `hitTestResults[0]`: 我们获取第一个命中结果。 在更复杂的场景中,您可能需要遍历所有结果并选择最合适的。
- `hit.getPose(xrReferenceSpace)`: 检索指定参考空间中命中的姿势(位置和方向)。
- `object3D.position.set(...)` 和 `object3D.quaternion.set(...)`: 更新虚拟对象 (object3D) 的位置和方向以匹配命中姿势。 这会将对象放置在交点处。
- `object3D.visible = true/false`: 控制虚拟对象的可见性,使其仅在找到命中时出现。
第 3 步:设置您的 3D 场景(Three.js 示例)
此示例使用 Three.js,这是一个流行的 JavaScript 3D 库,用于创建一个带有立方体的简单场景。 您可以将其改编为使用其他库,例如 Babylon.js 或 A-Frame。
let scene, camera, renderer, object3D;
let xrSession, xrReferenceSpace, xrHitTestSource;
function initRenderer() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.xr.enabled = true; // Enable WebXR
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1); // 10cm cube
const material = new THREE.MeshNormalMaterial();
object3D = new THREE.Mesh(geometry, material);
object3D.visible = false; // Initially hide the object
scene.add(object3D);
renderer.setAnimationLoop(() => { /* No animation loop here. WebXR controls it.*/ });
renderer.xr.setSession(xrSession);
camera.position.z = 2; // Move the camera back
}
// Call initXR() to start the WebXR experience
initXR();
重要提示: 确保在您的 HTML 文件中包含 Three.js 库:
<script src="https://threejs.org/build/three.js"></script>
高级技术和优化
上面的基本实现为 WebXR 命中测试提供了一个基础。 以下是在构建更复杂的体验时要考虑的一些高级技术和优化:
1. 过滤命中测试结果
在某些情况下,您可能希望过滤命中测试结果,仅考虑特定类型的表面。 例如,您可能只想允许将对象放置在水平表面(地板或桌子)上。 您可以通过检查命中姿势的法线向量并将其与向上向量进行比较来实现此目的。
if (hitTestResults.length > 0) {
const hit = hitTestResults[0];
const hitPose = hit.getPose(xrReferenceSpace);
// Check if the surface is approximately horizontal
const upVector = new THREE.Vector3(0, 1, 0); // World up vector
const hitNormal = new THREE.Vector3();
hitNormal.set(hitPose.transform.orientation.x, hitPose.transform.orientation.y, hitPose.transform.orientation.z);
hitNormal.applyQuaternion(camera.quaternion); // Rotate the normal to world space
const dotProduct = upVector.dot(hitNormal);
if (dotProduct > 0.9) { // Adjust the threshold (0.9) as needed
// Surface is approximately horizontal
object3D.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z);
object3D.quaternion.set(hitPose.transform.orientation.x, hitPose.transform.orientation.y, hitPose.transform.orientation.z, hitPose.transform.orientation.w);
object3D.visible = true;
} else {
object3D.visible = false;
}
}
2. 使用瞬时输入源
对于更高级的输入方法(如手部跟踪),您通常会使用瞬时输入源。 瞬时输入源表示临时输入事件,例如手指轻敲或手势。 WebXR 输入 API 允许您访问这些事件并基于用户的手部位置创建命中测试源。
xrSession.addEventListener('selectstart', (event) => {
const inputSource = event.inputSource;
const targetRayPose = event.frame.getPose(inputSource.targetRaySpace, xrReferenceSpace);
if (targetRayPose) {
// Create a hit test source from the target ray pose
xrSession.requestHitTestSourceForTransientInput({ targetRaySpace: inputSource.targetRaySpace, profile: inputSource.profiles }).then((hitTestSource) => {
const hitTestResults = event.frame.getHitTestResults(hitTestSource);
if (hitTestResults.length > 0) {
const hit = hitTestResults[0];
const hitPose = hit.getPose(xrReferenceSpace);
// Place an object at the hit location
const newObject = new THREE.Mesh(new THREE.SphereGeometry(0.05, 32, 32), new THREE.MeshNormalMaterial());
newObject.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z);
scene.add(newObject);
}
hitTestSource.cancel(); // Cleanup the hit test source
});
}
});
3. 优化性能
WebXR 体验可能需要大量计算,尤其是在移动设备上。 以下是一些优化性能的技巧:
- 减少命中测试的频率: 每帧执行命中测试可能会很昂贵。 考虑降低频率,尤其是在用户移动缓慢的情况下。 您可以使用计时器或仅在用户启动操作时执行命中测试。
- 使用边界体积层次结构 (BVH): 如果您有一个包含许多对象的复杂场景,使用 BVH 可以显着加快碰撞检测速度。 Three.js 和 Babylon.js 提供 BVH 实现。
- LOD(细节级别): 根据 3D 模型与相机的距离使用不同细节级别。 这减少了需要渲染的远距离对象的面数。
- 遮挡剔除: 不要渲染隐藏在其他对象后面的对象。 这可以显着提高复杂场景中的性能。
4. 处理不同的参考空间
WebXR 支持不同的参考空间,这些参考空间定义了用于跟踪用户位置和方向的坐标系。 最常见的参考空间是:
- 本地: 坐标系的原点相对于用户的起始位置固定。 这适用于用户停留在小区域内的体验。
- 有界地板: 原点位于地板水平面,XZ 平面表示地板。 这适用于用户可以在房间内移动的体验。
- 无界: 原点不固定,用户可以自由移动。 这适用于大规模的 AR 体验。
选择合适的参考空间对于确保您的 WebXR 体验在不同环境中正常工作非常重要。 您可以在创建 XR 会话时请求特定的参考空间。
xrReferenceSpace = await xrSession.requestReferenceSpace('bounded-floor');
5. 处理设备兼容性
WebXR 是一项相对较新的技术,并非所有浏览器和设备都同样支持它。 在尝试初始化 WebXR 会话之前,检查 WebXR 支持非常重要。
if (navigator.xr) {
// WebXR is supported
initXR();
} else {
// WebXR is not supported
console.error('WebXR is not supported in this browser.');
}
您还应该在各种设备上测试您的 WebXR 体验,以确保其正常运行。
国际化考虑事项
为全球受众开发 WebXR 应用程序时,考虑国际化 (i18n) 和本地化 (l10n) 非常重要。
- 文本和 UI 元素: 使用本地化库将文本和 UI 元素翻译成不同的语言。 确保您的 UI 布局可以容纳不同的文本长度。 例如,德语单词往往比英语单词长。
- 计量单位: 对不同的地区使用适当的计量单位。 例如,在大多数国家/地区使用米和公里,但在美国和英国使用英尺和英里。 允许用户选择他们喜欢的计量单位。
- 日期和时间格式: 对不同的地区使用适当的日期和时间格式。 例如,在某些国家/地区使用 YYYY-MM-DD 格式,在其他国家/地区使用 MM/DD/YYYY 格式。
- 货币: 以适当的格式显示不同地区的货币。 使用库来处理货币转换。
- 文化敏感性: 了解文化差异,避免使用可能冒犯某些文化的图像、符号或语言。 例如,某些手势在不同的文化中可能具有不同的含义。
WebXR 开发工具和资源
一些工具和资源可以帮助您进行 WebXR 开发:
- Three.js: 一个流行的 JavaScript 3D 库,用于创建基于 WebGL 的体验。
- Babylon.js: 另一个功能强大的 JavaScript 3D 引擎,侧重于 WebXR 支持。
- A-Frame: 一个使用 HTML 构建 VR 体验的 Web 框架。
- WebXR 模拟器: 一个浏览器扩展,允许您在不需要物理 VR 或 AR 设备的情况下测试 WebXR 体验。
- WebXR 设备 API 规范: W3C 提供的官方 WebXR 规范。
- Mozilla Mixed Reality 博客: 了解 WebXR 和相关技术的好资源。
结论
WebXR 命中测试管理器是一个强大的工具,用于创建交互式和沉浸式 AR/VR 体验。 通过理解光线投射和命中测试 API 的概念,您可以构建引人注目的应用程序,允许用户以自然直观的方式与虚拟世界交互。 随着 WebXR 技术的不断发展,创建创新和引人入胜的体验的可能性是无穷无尽的。 请记住为性能优化您的代码,并在为全球受众开发时考虑国际化。 拥抱构建下一代沉浸式 Web 体验的挑战和回报。